package com.typhoon.spring_boot_demo1.utils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;

public class BeanUtils {
	private static final Logger log=LoggerFactory.getLogger(CollectionUtils.class);

    public static String describe(Object obj) {
        return JSON.toJSONString(obj);
    }

    public static String describePretty(Object obj) {
        return JSON.toJSONString(obj, true);
    }

    public static Map<String, Object> toMap(Object bean) {
        if (bean instanceof Map) {
            return ((Map) bean);
        }
        ObjectMapper m = new ObjectMapper();
        m.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        Map map = (Map) m.convertValue(bean, Map.class);
        return map;
    }

    public static <T> T toBean(Map<String, Object> map, Class<T> clazz) {
        ObjectMapper m = new ObjectMapper();
        m.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        return m.convertValue(map, clazz);
    }

    public static <T> T toBean(Object source, Class<T> clazz) {
        if (source == null)
            return null;
        try {
            String jsonstr = JSON.toJSONString(source);
            return JSON.parseObject(jsonstr, clazz);
        } catch (RuntimeException e) {
            log.error("", e);
        }
        return null;
    }

    public static <T> T toBean(Object source, Class<T> clazz, String[] ignoreProperties) {
        try {
            T t = clazz.newInstance();
            copyProperties(t, source, ignoreProperties);
            return t;
        } catch (InstantiationException e) {
            log.error("", e);
        } catch (IllegalAccessException e) {
            log.error("", e);
        }
        return null;
    }

    public static void copyProperties(Object dest, Object orig) {
        try {
            org.springframework.beans.BeanUtils.copyProperties(orig, dest);
        } catch (Exception e) {
            log.debug("copyProperties() error:" + dest.getClass() + "'", e);
        }
    }

    public static void copyProperties(Object dest, Object orig, String[] ignoreProperties) {
        try {
            org.springframework.beans.BeanUtils.copyProperties(orig, dest, ignoreProperties);
        } catch (Exception e) {
            log.debug("copyProperties() error:" + dest.getClass() + "'", e);
        }
    }

    public static void copyNotNullProperties(Object dest, Object orig) {
        try {
            copyProperties(dest, orig, false, false);
        } catch (Exception e) {
            if (log.isDebugEnabled())
                log.debug("copyProperties() error:" + dest.getClass() + "'", e);
        }
    }

    public static void copyToNullProperties(Object dest, Object orig) {
        try {
            copyProperties(dest, orig, true, false);
        } catch (Exception e) {
            if (log.isDebugEnabled())
                log.debug("copyProperties() error:" + dest.getClass() + "'", e);
        }
    }

    public static void copyNNToNullProperties(Object dest, Object orig) {
        try {
            copyProperties(dest, orig, false, false);
        } catch (Exception e) {
            if (log.isDebugEnabled())
                log.debug("copyProperties() error:" + dest.getClass() + "'", e);
        }
    }

    public static void copyNNToNNProperties(Object dest, Object orig) {
        try {
            copyProperties(dest, orig, false, true);
        } catch (Exception e) {
            if (log.isDebugEnabled())
                log.debug("copyProperties() error:" + dest.getClass() + "'", e);
        }
    }

    public static void copyProperties(Object dest, Object orig, boolean copyNull, boolean copyToNotNull)
            throws Exception {
        if (dest == null) {
            throw new IllegalArgumentException("No destination bean specified");
        }
        if (orig == null) {
            throw new IllegalArgumentException("No origin bean specified");
        }
        PropertyDescriptor[] origDescriptors = PropertyUtils.getPropertyDescriptors(orig);
        for (int i = 0; i < origDescriptors.length; ++i) {
            String name = origDescriptors[i].getName();
            if ((!(PropertyUtils.isReadable(orig, name))) || (!(PropertyUtils.isWriteable(dest, name)))) continue;
            try {
                Object value = PropertyUtils.getSimpleProperty(orig, name);
                if ((value != null) || (copyNull)) {
                    if (copyToNotNull) {
                        Object destFieldValue = PropertyUtils.getSimpleProperty(dest, name);
                        if (destFieldValue == null) ;
                    }

                    PropertyUtils.setSimpleProperty(dest, name, value);
                }
            } catch (NoSuchMethodException e) {
                if (log.isDebugEnabled())
                    log.debug("Error writing to '" + name + "' on class '" + dest.getClass() + "'", e);
            }
        }
    }

    public static void initProperties(Object bean) {
        PropertyDescriptor[] propertyDescriptors = PropertyUtils.getPropertyDescriptors(bean);
        if (ArrayUtils.isEmpty(propertyDescriptors)) {
            return;
        }

        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            String name = propertyDescriptor.getName();
            try {
                Object value = PropertyUtils.getSimpleProperty(bean, name);
                if (value == null) {
                    if (propertyDescriptor.getPropertyType().isAssignableFrom(Boolean.class)) {
                        PropertyUtils.setSimpleProperty(bean, name, Boolean.valueOf(false));
                    }

                    if (propertyDescriptor.getPropertyType().isAssignableFrom(String.class)) {
                        PropertyUtils.setSimpleProperty(bean, name, "");
                    }

                    if (propertyDescriptor.getPropertyType().isAssignableFrom(Number.class))
                        PropertyUtils.setSimpleProperty(bean, name, Integer.valueOf(0));
                }
            } catch (Exception e) {
                log.warn("", e);
            }
        }
    }

    public static Map<String, Object> comparePojo(Object src, Object dest) {
        if (dest == null) {
            return null;
        }
        if (src != null && !dest.getClass().equals(src.getClass())) {
            return null;
        }
        Field[]  fs         = dest.getClass().getDeclaredFields();
        Class<?> superClass = dest.getClass().getSuperclass();
        while (superClass != null && superClass != Object.class) {
            fs = org.apache.commons.lang3.ArrayUtils.addAll(fs, superClass.getDeclaredFields());
            superClass = superClass.getSuperclass();
        }
        Map<String, Object> valuesMap = new HashMap<String, Object>();
        for (int i = 0; i < fs.length; i++) {
            String name  = fs[i].getName();
            Object value = null;
            if ("serialVersionUID".equals(name))
                continue;
            Object v1 = null;
            Object v2 = null;
            try {
                Method m = dest.getClass().getMethod("get" + StringUtils.capitalize(name));
                v1 = m.invoke(dest);
                if (src != null) {
                    v2 = m.invoke(src);
                }
            } catch (Exception e) {
                System.err.println(e + " : name=" + name + ",value=" + value);
            }
            if (v1 == null && v2 != null) {
                continue;
            }
            if (v1 != null && !v1.equals(v2)) {
               /* if (v1 instanceof Date) {
                    valuesMap.put(name, DATE_FORMAT.format(v1));
                } else {
                    valuesMap.put(name, v1.toString());
                }*/
                valuesMap.put(name, v1);
            }
        }
        return valuesMap;
    }

    public static Map<String, Object> comparePojo(Object src, Object dest, boolean allowNull) {
        if (dest == null) {
            return null;
        }
        if (src != null && !dest.getClass().equals(src.getClass())) {
            return null;
        }
        Field[]  fs         = dest.getClass().getDeclaredFields();
        Class<?> superClass = dest.getClass().getSuperclass();
        while (superClass != null && superClass != Object.class) {
            fs = org.apache.commons.lang3.ArrayUtils.addAll(fs, superClass.getDeclaredFields());
            superClass = superClass.getSuperclass();
        }
        Map<String, Object> valuesMap = new HashMap();
        for (int i = 0; i < fs.length; i++) {
            String name  = fs[i].getName();
            Object value = null;
            if ("serialVersionUID".equals(name))
                continue;
            Object v1 = null;
            Object v2 = null;
            try {
                Method m = dest.getClass().getMethod("get" + StringUtils.capitalize(name));
                v1 = m.invoke(dest);
                if (src != null) {
                    v2 = m.invoke(src);
                }
            } catch (Exception e) {
                System.err.println(e + " : name=" + name + ",value=" + value);
            }
            if (v1 == null && v2 != null) {
                if (allowNull) {
                    valuesMap.put(name, v1);
                } else {
                    continue;
                }
            }
            if (v1 != null && !v1.equals(v2)) {
                /*if (v1 instanceof Date) {
                    valuesMap.put(name, DATE_FORMAT.format(v1));
                } else {
                    valuesMap.put(name, v1.toString());
                }*/
                valuesMap.put(name, v1);
            }
        }
        return valuesMap;
    }

    /**
     * 比较两个同类型的对象,把dest对象里面与src对象里不同的值记录到map中
     *
     * @param src           源对象
     * @param dest          目标对象
     * @param excludeFields 排除字段列表，多个字段已","分割，src和dest对应的字段值虽然不同，但是不做更新,忽略大小写
     * @return map
     */
    public static Map<String, Object> comparePojo(Object src, Object dest, String excludeFields) {
        if (dest == null) {
            return null;
        }
        if (src != null && !dest.getClass().equals(src.getClass())) {
            return null;
        }
        Map<String, Boolean> excludeMap = new HashMap<String, Boolean>();
        if (excludeFields != null && excludeFields.length()>0) {
            for (String f : excludeFields.split(",")) {
                excludeMap.put(f.toLowerCase(), true);
            }
        }

        Field[]  fs         = dest.getClass().getDeclaredFields();
        Class<?> superClass = dest.getClass().getSuperclass();
        while (superClass != null && superClass != Object.class) {
            fs = (Field[])ArrayUtils.addAll(fs, superClass.getDeclaredFields());
            superClass = superClass.getSuperclass();
        }
        Map<String, Object> valuesMap = new HashMap();
        for (int i = 0; i < fs.length; i++) {
            String name  = fs[i].getName();
            Object value = null;
            if ("serialVersionUID".equals(name))
                continue;
            Object v1 = null;
            Object v2 = null;
            try {
                Method m = dest.getClass().getMethod("get" + StringUtils.capitalize(name));
                v1 = m.invoke(dest);
                if (src != null) {
                    v2 = m.invoke(src);
                }
            } catch (Exception e) {
                System.err.println(e + " : name=" + name + ",value=" + value);
            }
            if (v1 == null && v2 != null) {
                continue;
            }
            if (v1 != null && !v1.equals(v2)) {
                // 如果该字段在排除列表里面则跳过
                if(excludeMap.containsKey(name.toLowerCase())){
                    continue;
                }
                valuesMap.put(name,v1);
                /*if (v1 instanceof Date) {
                    valuesMap.put(name, DATE_FORMAT.format(v1));
                } else {
                    valuesMap.put(name, v1);
                }*/
            }
        }
        return valuesMap;
    }
}